﻿#include "TcpClient.h"
#include <QtNetwork>
#include <QDebug>
#include <iostream>
#include <memory>
#include <QtCore>
#include "qmath.h"

//Boost
//#include <boost/smart_ptr.hpp>


//JSON
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h" // for stringify JSON
using namespace rapidjson;


const int TcpClient::MaxBufferSize = 1024 * 1024 * 20;              //接收缓冲区长度
char* TcpClient::messageCharArray  = new char[MaxBufferSize];      //接收缓冲区
//char* TcpClient::header = new char[58];


bool TcpClient::sendComplete       = true;
int TcpClient::sendAlready         = 0; 
int TcpClient::sendRestLength      = 0;
int TcpClient::sendMaxSizePerTime = 1024;

TcpClient::TcpClient(QObject *parent)
	:QObject(parent)
	,tcpSocket(new QTcpSocket(this))
	,networkSession(Q_NULLPTR)
{
	initParam();
	tcpSocket->setSocketOption(QAbstractSocket::SocketOption::KeepAliveOption, socketOption);
	tcpSocket->setSocketOption(QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption, recvBufferSize);


	typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
	connect(tcpSocket, static_cast<QAbstractSocketErrorSignal>(&QAbstractSocket::error),
		this, &TcpClient::displayError);
	connect(tcpSocket, &QIODevice::readyRead, this, &TcpClient::readFortune);
	connect(tcpSocket, &QAbstractSocket::connected, this, &TcpClient::connectSuccess);
}

TcpClient::TcpClient(const QString ipAddress, quint16 port, QObject *parent)
	:QObject(parent)
	,serverIpAddress(ipAddress)
	,serverPort(port)
	,tcpSocket(new QTcpSocket(this))
	, networkSession(Q_NULLPTR)
{
	initParam();

	tcpSocket->setSocketOption(QAbstractSocket::SocketOption::KeepAliveOption, socketOption);
	tcpSocket->setSocketOption(QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption, recvBufferSize);


	typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
	connect(tcpSocket, static_cast<QAbstractSocketErrorSignal>(&QAbstractSocket::error),
		this, &TcpClient::displayError);

	connect(tcpSocket, &QIODevice::readyRead, this, &TcpClient::readFortune);
	connect(tcpSocket, &QAbstractSocket::connected, this, &TcpClient::connectSuccess);
}

TcpClient::~TcpClient()
{

}

//连接服务器
void TcpClient::requestNewFortune() 
{
	tcpSocket->abort();
	tcpSocket->connectToHost(serverIpAddress, serverPort);
}

/**************************************************************************************
**********************                读取消息             ****************************
**************************************************************************************/
#pragma region 从服务器端读取消息
void TcpClient::readFortune()
{
	boost::scoped_array<char> header(new char[57 + 1]);
	header[57] = '\0';

	static int restMessageLength = 0;
	int protocal = 0;
	int number = 0;
	int length = 0;


	if (isReadHeader)
	{
		static int restLength = 57;
		static int alreadyReadLength = 0;
		while (restLength)
		{
			int readLength = tcpSocket->read(header.get() + alreadyReadLength, restLength);
			if (readLength != 0)
			{
				restLength -= readLength;
				alreadyReadLength += readLength;
			}
			else
			{
				break;
			}

			if (restLength == 0)
			{
				isReadHeader = false;
				restLength = 57;
				alreadyReadLength = 0;

				//TODO:解析数据头
				//(1):获取消息的总长度
				int messageTotalLength = (header[0] & 0xff) << 24 | (header[1] & 0xff) << 16 | (header[2] & 0xff) << 8 | (header[3] & 0xff);

				memcpy(header.get(), header.get() + 4, 53);
				header[53] = '\0';
				Document doc;
				ParseResult ok =  doc.Parse(header.get());
				if (!ok)
				{
					break;
				}
				assert(doc.IsObject());
				protocal = doc["Protocal"].GetInt();
				number = doc["Number"].GetInt();
				length = doc["Length"].GetInt();
			
				restMessageLength = length;
				messageCharArray[restMessageLength] = '\0';

				break;
			}
		}
	}

	if (!isReadHeader)  //read message
	{
		static int alreadyReadMessageLength = 0;
		static char tmpBuffer[4096];
		while (restMessageLength)
		{
			int tmplength = qMin(restMessageLength, 4096);
			int readLength = tcpSocket->read(tmpBuffer, tmplength);
			if (readLength != 0)
			{
				memcpy(messageCharArray + alreadyReadMessageLength, tmpBuffer, readLength);
				restMessageLength -= readLength;
				alreadyReadMessageLength += readLength;
			}
			else
			{
				break;
			}

			if (0 == restMessageLength)
			{
				isReadHeader = true;
				alreadyReadMessageLength = 0;

				//TODO:将接收到的消息放入消息队列
				MessageBuffer::MessageType messageItem;
				messageItem.first = protocal;
				messageItem.second = std::string(messageCharArray);
				emit sendMessage(messageItem);
				break;
			}
		}
	}
}
#pragma endregion




void TcpClient::displayError(QAbstractSocket::SocketError socketError)
{
	switch (socketError) {
	case QAbstractSocket::RemoteHostClosedError:
		emit sendSocketError(QAbstractSocket::RemoteHostClosedError,
			tr("The remote host has been closed!"));
		break;
	case QAbstractSocket::HostNotFoundError:
		emit sendSocketError(QAbstractSocket::HostNotFoundError, 
			tr("The host was not found. Please check the "
			"host name and port settings."));
		break;
	case QAbstractSocket::ConnectionRefusedError:
		emit sendSocketError(QAbstractSocket::HostNotFoundError,
			tr("The connection was refused by the peer. "
				"Make sure the fortune server is running, "
				"and check that the host name and port "
				"settings are correct."));
		break;
	default:
		emit sendSocketError(QAbstractSocket::NetworkError,
			tr("The following error occurred: %1.")
			.arg(tcpSocket->errorString()));
	}
	

}

void TcpClient::sessionOpened()
{
	// Save the used configuration
	QNetworkConfiguration config = networkSession->configuration();
	QString id;
	if (config.type() == QNetworkConfiguration::UserChoice)
		id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString();
	else
		id = config.identifier();

	QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
	settings.beginGroup(QLatin1String("QtNetwork"));
	settings.setValue(QLatin1String("DefaultNetworkConfiguration"), id);
	settings.endGroup();

}

void TcpClient::sendMessageTimerSlot()
{
	if (tcpSocket->isWritable()) 
	{
		if (!sendComplete) //如果上一帧数据没有发送完，则继续发送上一帧数据
		{
			while (sendRestLength)
			{
				int sendLength = tcpSocket->write(sendMessageItem.c_str() + sendAlready, qMin(sendRestLength, sendMaxSizePerTime));
				if (sendLength != 0)
				{
					sendAlready += sendLength;
					sendRestLength -= sendLength;
				}
				else
				{
					sendComplete = false;
					break;
				}
				if (0 == sendRestLength)
				{
					sendAlready = 0;
					sendComplete = true;
					emit displaySendMessageContent(messageitem.first, sendMessageItem.c_str());
				}
			}
		}
		else //上一帧数据发送成功
		{
			//取出一条数据
			if (-1 != MessageBuffer::getMessageBuffer()->send_pop(messageitem))
			{
				char* header = MessageBuffer::getMessageBuffer()->generateHeader(messageitem.first, 1,messageitem.second.length());
				sendMessageItem = std::string(header) + std::string(messageitem.second);
				sendRestLength = sendMessageItem.length();
				while (sendRestLength)
				{
					int sendLength = tcpSocket->write(sendMessageItem.c_str() + sendAlready, qMin(sendRestLength, sendMaxSizePerTime));
					if (sendLength != 0)
					{
						sendAlready += sendLength;
						sendRestLength -= sendLength;
					}
					else
					{
						sendComplete = false;
						break;
					}

					if (0 == sendRestLength)
					{
						sendAlready = 0;
						sendComplete = true;
						emit displaySendMessageContent(messageitem.first, sendMessageItem.c_str());
					}
				}

			}
			/*else
				emit displaySendMessageContent(0, "empty sendMessageQueue");*/
		}
		
	}
	
}

void TcpClient::connectSuccess()
{
	emit sendConnectSuccess();
}

void TcpClient::sendMessageToServer(const MessageBuffer::MessageType & messageitem)
{
	if (tcpSocket->isWritable())
	{
		if (!sendComplete) //如果上一帧数据没有发送完，则继续发送上一帧数据
		{
			while (sendRestLength)
			{
				int sendLength = tcpSocket->write(message.get() + sendAlready, qMin(sendRestLength, sendMaxSizePerTime));
				if (sendLength != 0)
				{
					sendAlready += sendLength;
					sendRestLength -= sendLength;
				}
				else
				{
					sendComplete = false;
					break;
				}
				if (0 == sendRestLength)
				{
					sendAlready = 0;
					sendComplete = true;
					message.reset();
					emit displaySendMessageContent(messageitem.first, message.get());
				}
			}
		}
		else //上一帧数据发送成功
		{
			
			char* header = MessageBuffer::getMessageBuffer()->generateHeader(messageitem.first, 1, messageitem.second.length());
			int messageTotalLength = strlen(header) + messageitem.second.length();
			
			message = boost::shared_array<char>(new char[messageTotalLength+sizeof(int)]);
			char length[sizeof(int)];
			for (int i = 0; i < sizeof(int); i++)
			{
				length[i] = messageTotalLength >> (sizeof(int) -1 - i) * 8 & 0xff;
			}
			memcpy(message.get(), length, sizeof(int));
			memcpy(message.get() + sizeof(int), (std::string(header) + std::string(messageitem.second)).c_str(), messageTotalLength);
			//sendMessageItem = std::string(header) + std::string(messageitem.second);
			//sendRestLength = sendMessageItem.length();
			sendRestLength = messageTotalLength+4;
			while (sendRestLength)
			{
				//int sendLength = tcpSocket->write(sendMessageItem.c_str() + sendAlready, qMin(sendRestLength, sendMaxSizePerTime));
				int sendLength = tcpSocket->write(message.get() + sendAlready, qMin(sendRestLength, sendMaxSizePerTime));
				if (sendLength != 0)
				{
					sendAlready += sendLength;
					sendRestLength -= sendLength;
				}
				else
				{
					sendComplete = false;
					break;
				}
				
				if (0 == sendRestLength)
				{
					sendAlready = 0;
					sendComplete = true;
					message.reset();
					emit displaySendMessageContent(messageitem.first, sendMessageItem.c_str());
				}
			}
		}
	}
}

void TcpClient::closeConnection()
{
	tcpSocket->close();
}

void TcpClient::initParam()
{
	isReadHeader = true;
	subscribeMessage_ = "{\"topic_number\":0,\"spilt_char\":\",\",\"topic\":\"\",\"topicID\":[],\"numbers\":[]}";
	messageCharArray = new char[MaxBufferSize];        //接收缓冲区
	//header = new char[58];


	/*tcpSocket->setSocketOption(QAbstractSocket::SocketOption::KeepAliveOption, socketOption);
	tcpSocket->setSocketOption(QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption, recvBufferSize);


	typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
	connect(tcpSocket, static_cast<QAbstractSocketErrorSignal>(&QAbstractSocket::error),
		this, &TcpClient::displayError);

	connect(tcpSocket, &QIODevice::readyRead, this, &TcpClient::readFortune);
	connect(tcpSocket, &QAbstractSocket::connected, this, &TcpClient::connectSuccess);*/
}
void TcpClient::setServerAddress(QString serverAddress, quint16 port)
{
	serverIpAddress = serverAddress;
	serverPort = port;
}


QString TcpClient::getLocalAddress()
{
	return tcpSocket->localAddress().toString();
}

void TcpClient::timerEvent(QTimerEvent *timerEvent)
{
	MessageBuffer::MessageType messageitem;
	MessageBuffer::getMessageBuffer()->pop(messageitem);
}

void TcpClient::resetParam()
{
	isReadHeader = true;
	sendComplete = true;
	sendAlready  = 0;
	sendRestLength = 0;
	sendMessageItem.clear();
	recvBufferSize = 1024 * 1024 * 512;
}

int TcpClient::registerTopic(int topic_id, const char* topic_name)
{
	boost::scoped_array<char> outbuffer(new char[256]);

	Document doc;
	doc.Parse(subscribeMessage_);
	assert(doc.IsObject());

	//消息的个数
	doc["topic_number"] = 1;

	//消息名称
	Value& topicNameArray = doc["topic"];
	topicNameArray.SetString(topic_name, strlen(topic_name));

	//消息的类型
	Value& topicIDArray = doc["topicID"];
	Document::AllocatorType& topicIDAllocator = doc.GetAllocator();
	topicIDArray.PushBack(topic_id, topicIDAllocator);

	//消息的最大个数
	Value& numbersArray = doc["numbers"];
	Document::AllocatorType& numbersAllocator = doc.GetAllocator();
	numbersArray.PushBack(1, numbersAllocator);

	StringBuffer buffer;
	Writer<StringBuffer> writer(buffer);
	doc.Accept(writer);

	strncpy(outbuffer.get(), buffer.GetString(), buffer.GetSize());
	outbuffer[buffer.GetSize()] = '\0';


	MessageBuffer::MessageType messageItem;
	messageItem.first = 0;
	messageItem.second = outbuffer.get();
	emit sendMessageToServer(messageItem);
	return buffer.GetSize();
}
